﻿using System;
using System.Linq;

namespace FeedSyncLibrarySLTest
{
    public partial class MainPage : System.Windows.Controls.UserControl
    {
        private System.Collections.Generic.Dictionary<string, string> m_NamespaceDictionary = new System.Collections.Generic.Dictionary<string, string>();

        public MainPage()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            this.RunTests();
        }

        private void RunTests()
        {
            string FeedContents = "<?xml version='1.0' encoding='utf-8'?>";
            FeedContents += " <rss xmlns:sx='http://feedsync.org/2007/feedsync' version='2.0'>";
            FeedContents += "  <channel>";
            FeedContents += "   <title>todolist.xml</title>";
            FeedContents += "   <description>Sample to do list</description>";
            FeedContents += "   <link>http://www.example.com/index.html</link>";
            FeedContents += "   <item>";
            FeedContents += "    <title>Pay Bills</title>";
            FeedContents += "    <description>Electric, Gas and Water Bills</description>";
            FeedContents += "    <sx:sync id='101' updates='1' deleted='false' noconflicts='false'>";
            FeedContents += "     <sx:history sequence='1' when='2007-11-29T00:27:11-06:00' by='fsRSSConvert.js' />";
            FeedContents += "    </sx:sync>";
            FeedContents += "   </item>";
            FeedContents += "  </channel>";
            FeedContents += " </rss>";

            System.Xml.Linq.XDocument XDocument1 = null;
            System.Xml.Linq.XDocument XDocument2 = null;
            Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeed RSSFeed1 = null;
            Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeed RSSFeed2 = null;
            Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeedSyncItem RSSFeedSyncItem = null;

            #region TEST 1 - Create new nodes

            try
            {
                //  Create XDocument to simulate local FeedSync Feed
                XDocument1 = System.Xml.Linq.XDocument.Parse(FeedContents);
                RSSFeed1 = new Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeed(XDocument1);

                RSSFeedSyncItem = new Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeedSyncItem
                    (
                    RSSFeed1.XDocument,
                    "EP1",
                    "ep1.100",
                    "Buy groceries",
                    "Get milk and eggs"
                    );

                //  Add item to feed
                RSSFeed1.AddItem(RSSFeedSyncItem);

                RSSFeedSyncItem = new Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeedSyncItem
                    (
                    RSSFeed1.XDocument,
                    "EP1",
                    "ep1.101",
                    "Get car serviced",
                    "Needs oil change"
                    );

                //  Add item to feed
                RSSFeed1.AddItem(RSSFeedSyncItem);

                //  Create XDocument to simulate remote FeedSync Feed
                XDocument2 = System.Xml.Linq.XDocument.Parse(FeedContents);
                RSSFeed2 = new Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeed(XDocument2);

                RSSFeedSyncItem = new Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeedSyncItem
                    (
                    RSSFeed2.XDocument,
                    "EP2",
                    "ep2.100",
                    "Return books to library",
                    "2 books in total"
                    );

                //  Add item to feed
                RSSFeed2.AddItem(RSSFeedSyncItem);

                RSSFeedSyncItem = new Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeedSyncItem
                    (
                    RSSFeed2.XDocument,
                    "EP2",
                    "ep2.101",
                    "Get birthday gift for mother",
                    "She likes blue flowers"
                    );

                //  Add item to feed
                RSSFeed2.AddItem(RSSFeedSyncItem);

                RSSFeed1.Merge(RSSFeed2);

                this.DisplayTestResults("Test 1 Results", RSSFeed1.XDocument);
            }
            catch (System.Exception Exception)
            {
                System.Diagnostics.Debug.Assert(false, "Exception in Test 1 - " + Exception.Message);
            }

            #endregion

            #region TEST 2 - Update with no conflict

            try
            {
                RSSFeedSyncItem = RSSFeed1.GetItem("ep1.100");
                RSSFeedSyncItem.Description = "Get milk, eggs and butter";
                RSSFeedSyncItem.Update(System.DateTime.Now, "EP1");

                RSSFeedSyncItem.Description = "Get milk, eggs, butter and bread";
                RSSFeedSyncItem.Update(System.DateTime.Now, "EP2");

                this.DisplayTestResults("Test 2 Results", RSSFeed1.XDocument);
            }
            catch (System.Exception Exception)
            {
                System.Diagnostics.Debug.Assert(false, "Exception in Test 2 - " + Exception.Message);
            }

            #endregion

            #region TEST 3 - Update with conflict

            try
            {
                XDocument2 = System.Xml.Linq.XDocument.Parse(XDocument1.ToString());
                RSSFeed2 = new Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeed(XDocument2);

                RSSFeedSyncItem = RSSFeed1.GetItem("ep1.100");
                RSSFeedSyncItem.Description = "Get milk, eggs, butter and rolls";
                RSSFeedSyncItem.Update(System.DateTime.Now, "EP1");

                RSSFeedSyncItem = RSSFeed2.GetItem("ep1.100");
                RSSFeedSyncItem.Title = "Buy Groceries - DONE";
                RSSFeedSyncItem.Description = "Get milk, eggs, butter and bread";

                //  Introduce delay in time to generate different "when" values
                System.Threading.Thread.Sleep(1000);

                RSSFeedSyncItem.Update(System.DateTime.Now, "EP2");

                RSSFeed1.Merge(RSSFeed2);

                this.DisplayTestResults("Test 3 Results", RSSFeed1.XDocument);
            }
            catch (System.Exception Exception)
            {
                System.Diagnostics.Debug.Assert(false, "Exception in Test 3 - " + Exception.Message);
            }

            #endregion

            #region TEST 4 - Resolve conflict

            try
            {
                RSSFeedSyncItem = RSSFeed1.GetItem("ep1.100");
                RSSFeedSyncItem.Title = "Buy Groceries - DONE";
                RSSFeedSyncItem.Description = "Get milk, eggs, butter and bread";

                RSSFeedSyncItem.ResolveConflicts(System.DateTime.Now, "EP2");

                this.DisplayTestResults("Test 4 Results", RSSFeed1.XDocument);
            }
            catch (System.Exception Exception)
            {
                System.Diagnostics.Debug.Assert(false, "Exception in Test 4 - " + Exception.Message);
            }

            #endregion

            #region TEST 5 - Delete with no conflicts

            try
            {
                XDocument2 = System.Xml.Linq.XDocument.Parse(XDocument1.ToString());
                RSSFeed2 = new Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeed(XDocument2);

                RSSFeedSyncItem = RSSFeed1.GetItem("ep1.101");
                RSSFeedSyncItem.Delete(System.DateTime.Now, "EP1");

                RSSFeedSyncItem = RSSFeed2.GetItem("ep2.101");
                RSSFeedSyncItem.Delete(System.DateTime.Now, "EP2");

                RSSFeed1.Merge(RSSFeed2);

                this.DisplayTestResults("Test 5 Results", RSSFeed1.XDocument);
            }
            catch (System.Exception Exception)
            {
                System.Diagnostics.Debug.Assert(false, "Exception in Test 5 - " + Exception.Message);
            }

            #endregion

            #region TEST 6 - Delete with conflicts

            try
            {
                XDocument2 = System.Xml.Linq.XDocument.Parse(XDocument1.ToString());
                RSSFeed2 = new Microsoft.CSA.FeedSync.FeedSyncLibrarySL.RSSFeed(XDocument2);

                RSSFeedSyncItem = RSSFeed1.GetItem("ep2.100");
                RSSFeedSyncItem.Delete(System.DateTime.Now, "EP1");

                //  Introduce delay in time to generate different "when" values
                System.Threading.Thread.Sleep(1000);

                RSSFeedSyncItem = RSSFeed2.GetItem("ep2.100");
                RSSFeedSyncItem.Delete(System.DateTime.Now, "EP2");

                RSSFeed1.Merge(RSSFeed2);

                this.DisplayTestResults("Test 6 Results", RSSFeed1.XDocument);
            }
            catch (System.Exception Exception)
            {
                System.Diagnostics.Debug.Assert(false, "Exception in Test 6 - " + Exception.Message);
            }

            #endregion

            System.Windows.Controls.TreeViewItem TreeViewItem = (ResultsTreeView.Items[0] as System.Windows.Controls.TreeViewItem);
            TreeViewItem.IsSelected = true;
            TreeViewItem.Focus();
        }

        private void CreateElements(System.Xml.Linq.XDocument i_XDocument, System.Windows.Controls.TreeViewItem i_ParentTreeViewItem)
        {
            System.Collections.Generic.Dictionary<string, string> NamespaceDictionary = new System.Collections.Generic.Dictionary<string, string>();

            var DescendentElements =
                from DescendantXNode in i_XDocument.DescendantNodes()
                where (DescendantXNode.NodeType == System.Xml.XmlNodeType.Element)
                select DescendantXNode;

            m_NamespaceDictionary.Clear();

            foreach (System.Xml.Linq.XElement DescendentElement in DescendentElements)
            {
                var Namespaces = from ElementAttribute in DescendentElement.Attributes()
                                 where (ElementAttribute.IsNamespaceDeclaration == true)
                                 select ElementAttribute;

                foreach (var Namespace in Namespaces)
                {
                    string NamespacePrefix = Namespace.Name.LocalName;
                    string NamespaceURI = Namespace.Value;

                    m_NamespaceDictionary[NamespaceURI] = NamespacePrefix;
                }
            }

            foreach (System.Xml.Linq.XElement XElement in i_XDocument.Elements())
            {
                this.CreateChildElements
                    (
                    XElement,
                    i_ParentTreeViewItem
                    );
            }
        }

        private void CreateChildElements(System.Xml.Linq.XElement i_XElement, System.Windows.Controls.TreeViewItem i_ParentTreeViewItem)
        {
            System.Windows.Controls.ItemCollection ItemCollection = null;

            if (i_ParentTreeViewItem == null)
                ItemCollection = ResultsTreeView.Items;
            else
                ItemCollection = i_ParentTreeViewItem.Items;

            System.Windows.Controls.TreeViewItem TreeViewItem = new System.Windows.Controls.TreeViewItem() { Tag = i_XElement };
            
            TreeViewItem.Expanded += TreeViewItem_Expanded;
            TreeViewItem.Collapsed += TreeViewItem_Collapsed;
            TreeViewItem.KeyUp += TreeViewItem_KeyUp;

            string ElementName = i_XElement.Name.LocalName;
            if (!System.String.IsNullOrEmpty(i_XElement.Name.NamespaceName))
                ElementName = m_NamespaceDictionary[i_XElement.Name.NamespaceName] + ":" + ElementName;
            
            TreeViewItem.Header = "<" + ElementName;

            foreach (System.Xml.Linq.XAttribute XAttribute in i_XElement.Attributes())
                TreeViewItem.Header += string.Format(" {0}=\"{1}\"", XAttribute.Name.LocalName, XAttribute.Value);

            var XNodes = 
                from XNode in i_XElement.Nodes() 
                where (XNode.NodeType == System.Xml.XmlNodeType.Text)
                select XNode;

            if ((XNodes.Count() > 0) || (i_XElement.HasElements))
                TreeViewItem.Header += ">";
            else
                TreeViewItem.Header += "/>";

            foreach (System.Xml.Linq.XText XText in XNodes)
                TreeViewItem.Header += XText.Value;

            if (i_XElement.HasElements)
            {
                foreach (System.Xml.Linq.XElement XElement in i_XElement.Elements())
                {
                    this.CreateChildElements
                        (
                        XElement,
                        TreeViewItem
                        );
                }
            }
            else if (XNodes.Count() > 0)
                TreeViewItem.Header += "</" + ElementName+ ">";

            ItemCollection.Add(TreeViewItem);
        }

        void TreeViewItem_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
        {
            System.Windows.Controls.TreeViewItem TreeViewItem = sender as System.Windows.Controls.TreeViewItem;

            if (e.Key == System.Windows.Input.Key.Enter)
            {
                TreeViewItem.IsExpanded = !TreeViewItem.IsExpanded;
                e.Handled = true;
            }
        }

        void TreeViewItem_Collapsed(object sender, System.Windows.RoutedEventArgs e)
        {
            System.Windows.Controls.TreeViewItem TreeViewItem = sender as System.Windows.Controls.TreeViewItem;
            System.Xml.Linq.XElement XElement = TreeViewItem.Tag as System.Xml.Linq.XElement;

            if (XElement.HasElements)
            {
                System.Windows.Controls.TreeViewItem ParentTreeViewItem = TreeViewItem.Parent as System.Windows.Controls.TreeViewItem;
                int Index = ParentTreeViewItem.Items.IndexOf(TreeViewItem);
                ParentTreeViewItem.Items.RemoveAt(Index + 1);
            }
        }

        void TreeViewItem_Expanded(object sender, System.Windows.RoutedEventArgs e)
        {
            System.Windows.Controls.TreeViewItem TreeViewItem = sender as System.Windows.Controls.TreeViewItem;
            System.Xml.Linq.XElement XElement = TreeViewItem.Tag as System.Xml.Linq.XElement;

            if (XElement.HasElements)
            {
                System.Windows.Controls.TreeViewItem ParentTreeViewItem = TreeViewItem.Parent as System.Windows.Controls.TreeViewItem;
                int Index = ParentTreeViewItem.Items.IndexOf(TreeViewItem);

                string ElementName = XElement.Name.LocalName;
                if (!System.String.IsNullOrEmpty(XElement.Name.NamespaceName))
                    ElementName = m_NamespaceDictionary[XElement.Name.NamespaceName] + ":" + ElementName;

                ParentTreeViewItem.Items.Insert(Index + 1, (new System.Windows.Controls.TreeViewItem() { Header = "</" + ElementName + ">" }));
            }
        }

        void DisplayTestResults(string i_Header, System.Xml.Linq.XDocument i_XDocument)
        {
            System.Windows.Controls.TreeViewItem TreeViewItem = new System.Windows.Controls.TreeViewItem() { Header = i_Header };
            TreeViewItem.KeyUp += TreeViewItem_KeyUp;
            ResultsTreeView.Items.Add(TreeViewItem);
            this.CreateElements(i_XDocument, TreeViewItem);
        }

    }
}
